library drawing;
import 'dart:html';
import 'dart:math';

class Point implements Hashable {
  final num x, y;
  Point(this.x, this.y);
  
  /** Generate a Point representing a ray of length 1 at the angle given by the
   * two points. */
  factory Point.unitRay(Point p1, Point p2) {
    // We only have eight directions available
    var a0 = p2.x - p1.x;
    var b0 = p2.y - p1.y;
    sign(i) => i == 0 ? 0 : i / i.abs();
    if (a0 == 0) {
      return new Point(0, sign(b0));
    }
    if (b0 == 0) {
      return new Point(sign(a0), 0);
    }
    // By Pythagoras' Theorem! a**2 + b**2 = c**2
    // with c = 1 and c**2 = 1 and a = b
    return new Point(pow(0.5, 0.5) * sign(a0),
        pow(0.5, 0.5) * sign(b0));
  }
  
  toString() => "[$x,$y]";
  
  /// The set of points adjacent to this one.
  adjacents() => [
      new Point(x - 1, y - 1),
      new Point(x - 1, y),
      new Point(x - 1, y + 1),
      new Point(x, y - 1),
      new Point(x, y + 1),
      new Point(x + 1, y - 1),
      new Point(x + 1, y),
      new Point(x + 1, y + 1),
  ];
  
  /// The set of points adjacent to this one in cardinal directions.
  cardinals() => [
      new Point(x - 1, y),
      new Point(x, y - 1),
      new Point(x, y + 1),
      new Point(x + 1, y),
  ];
  
  /// The distance between this point and the given point.
  dist(Point other) =>
      sqrt(pow(x - other.x, 2) + pow(y - other.y, 2));

  /** Whether the other point is adjacent to this one but not in a cardinal
   * direction. */
  cornerwiseTo(Point other) =>
      (other.x - x).abs() == 1 && (other.y - y).abs() == 1;

  get hashCode => ((x * 29) + y).toInt();
  equals(other) => other.x == x && other.y == y;
  operator==(other) => other.x == x && other.y == y;
}

class Rect implements Hashable {
  /** The two corners that define this rectangle.
   * Arranged so `a` is always top left and `b` is always top right. */
  final Point a, b;
  
  /** Create a Rect from two points. The points will be canonicalized */
  factory Rect(Point a, Point b) {
    num x1 = min(a.x, b.x);
    num x2 = min(a.x, b.x);
    num y1 = min(a.y, b.y);
    num y2 = min(a.y, b.y);
    return new Rect._(new Point(x1, y1), new Point(x2, y2));
    
  }
  
  Rect._(this.a, this.b);
  
  /// A collection of integral points wholly contained in this Rect. 
  List<Point> get points {
    var p = <Point>[];
    for (int x = a.x.ceil().toInt(); x <= b.x; x++) {
      for (int y = a.y.ceil().toInt(); y <= b.y; y++) {
        p.add(new Point(x, y));
      }
    }
    return p;
  }
  
  get hashCode => (a.hashCode() * 29) + b.hashCode();
}

/** Intersection of two lines. */
bool lineIntersect(Point v1, Point v2, Point v3, Point v4) {
  new Rect(v1, v2);
  // I don't understand this.
  // Taken from http://www.gamedev.net/topic/440350-2d-line-box-intersection/
  // -- copyright violation; find different solution later
  num denom = ((v4.y - v3.y) * (v2.x - v1.x)) - ((v4.x - v3.x) * (v2.y - v1.y));
  num numerator = ((v4.x - v3.x) * (v1.y - v3.y)) - ((v4.y - v3.y) * (v1.x - v3.x));

  num numerator2 = ((v2.x - v1.x) * (v1.y - v3.y)) - ((v2.y - v1.y) * (v1.x - v3.x));

  if (denom == 0.0) {
    return false;
  }
  num ua = numerator / denom;
  num ub = numerator2/ denom;

  return (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1);
}

/** Intersection of a rectangle and a line. */
bool rectIntersect(Rect rect, Point v1, Point v2) {
  var tl = rect.a;
  var tr = new Point(rect.b.x, rect.a.y);
  var br = rect.b;
  var bl = new Point(rect.a.x, rect.b.y);
  return lineIntersect(tl, tr, v1, v2) ||
      lineIntersect(tr, br, v1, v2) ||
      lineIntersect(br, bl, v1, v2) ||
      lineIntersect(bl, tl, v1, v2);
}

class Sprite implements Hashable {
  static int _id = 0;
  final int id;
  ImageElement img;
  Point pos;

  /** Unit point for the direction the sprite is facing. */
  Point dir;

  Sprite([this.img]) : id = _id++ {
    pos = new Point(0, 0);
    dir = new Point(1, 0);
  }

  get hashCode => id;
}